#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <cstdio>
#include <fcntl.h>

#define COMMANDLINESIZE 1024
#define CMD_PROMPT_FROMAT "[%s@%s %s]# "
#define CMD_PROMPT_SIZE 1024
#define MAX_PWD 1024
#define PWD_ENV_SZ 1024
#define SEP " "
#define ARGC_MAX 128
#define ENVS_SZ 100


#define NONE_REDIR 0
#define TNPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
std::string fileName;
int redirStatus = 0;

char* argv[ARGC_MAX];
int argc;

char* envs[ENVS_SZ];
//int envc;

int lastCode = 0;

const char* getHome()
{
    const char* home = getenv("HOME");
    return home == NULL?"NONE":home;
}

const char* getUserName()
{
    const char* name = getenv("USER");
    return name == NULL?"NONE":name;
}
const char* getPwd(char* pwd,int sz)
{
    //const char* pwd = getenv("PWD");
	if(getcwd(pwd,sz)==NULL)
		return  "NONE";
	char pwdEnv[PWD_ENV_SZ] = {0};
	snprintf(pwdEnv,sizeof(pwdEnv),"PWD=%s",pwd);
	putenv(pwdEnv);
    return pwd;
	//return pwd == NULL?"NONE":pwd;
}

void getHostName(char* name)
{
    if(gethostname(name,MAXHOSTNAMELEN))
        strcpy(name,"NONE");
}

std::string DirName(const char* pwd)
{
  #define SLASH "/"
  std::string dir = pwd;
  if(dir == SLASH) return SLASH;
  int pos = dir.rfind(SLASH);
  if(pos == -1) return "BUG";
  return dir.substr(pos+1);
}
void makeCmdPrompt(char* cmdPrompt)
{
    char hostName[MAXHOSTNAMELEN] = {0};
    getHostName(hostName);
	char pwd[MAX_PWD] = {0};
    snprintf(cmdPrompt,CMD_PROMPT_SIZE,CMD_PROMPT_FROMAT,getUserName(),hostName,DirName(getPwd(pwd,sizeof(pwd))).c_str());
}

void printCmdPrompt()
{
    char cmdPrompt[CMD_PROMPT_SIZE] = {0};
    makeCmdPrompt(cmdPrompt);
    printf("%s",cmdPrompt);
}
bool inputCmd(char * input)
{
    char * cin = fgets(input,COMMANDLINESIZE,stdin);
    if(cin == NULL)
        return false;
    input[strlen(input)-1] = 0;
    return strlen(input) != 0;
}

bool CmdParse(char* cmd)
{
    argc = 0;
    argv[argc++] = strtok(cmd,SEP);
    while((bool)(argv[argc++]=strtok(NULL,SEP)));
    argc--;
	if(argc == 0)
		return true;
    return false;
}
void printARG()
{
    for(int i = 0;i < argc;i++)
    {
        printf("%d:%s\n",i,argv[i]);
    }
    printf("%d\n",argc);
}

bool CheckAndBuiltin()
{
	std::string cmd = argv[0];
    if(cmd == "cd")
	{
		if(argc ==1)
		{
			std::string home = getHome();
			if(home.empty()) return true;
			chdir(home.c_str());
		}
		else
		{
			std::string where = argv[1];
			chdir(where.c_str());
		}

		return true;
	}
	else if(cmd == "echo")
	{
		if(argc ==2)
		{
			std::string opt = argv[1];
			if(opt == "$?")
			{
				std::cout<< lastCode<<std::endl;
				lastCode = 0;
				return true;
			}
			if(opt[0] == '$')
			{
				std::string env_name = opt.substr(1);
				char * env =  getenv(env_name.c_str());
				if(env != NULL)
				{
					std::cout<< getenv(env_name.c_str())<<std::endl;
					return true;
				}
			}
			std::cout<<opt<<std::endl;
		}
		return true;
	}
	return false;
}


void doCmd()
{
    pid_t id  = fork();
    if(id ==0)
    {
        //子进程
        int fd = -1;
        if(redirStatus == TNPUT_REDIR)
        {
           fd = open(fileName.c_str(),O_RDONLY,0666);
            if(fd < 0) exit(1);
            dup2(fd,0); 
        }
        else if( redirStatus== OUTPUT_REDIR)
        {
            fd = open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd < 0) exit(2);
            dup2(fd,1);
        }
        else if( redirStatus== APPEND_REDIR)
        {

            fd = open(fileName.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd < 0) exit(2);
            dup2(fd,1);
        }
        execvp(argv[0],argv);
        exit(1);
    }
    //父进程
	int status = 0;
    pid_t cId =  waitpid(id,&status,0);
	if(cId > 0)
	{
		lastCode = WEXITSTATUS(status);
	}
}

void InitEnv()
{
	extern char **environ;
	memset(envs,0,sizeof(envs));
	//envc = 0;
	int i = 0;
	for(;environ[i];i++)
	{
		envs[i] = (char *) malloc(strlen(environ[i])+1);
		strcpy(envs[i],environ[i]);
	}
	//envs[i] = "tewstsfdsadf=asdfsafsadf";
	//i++;
	envs[i] = NULL;
	for(i = 0;envs[i];i++)
	{
		putenv(envs[i]);
	}
	//putenv((char*)"asdfsaf=adsfasfjlkasjk");
}



void TrimSpace(char * str ,int * pwd)
{
    while(isspace(*(str+(*pwd))))
        (*pwd) ++; 
}

void RedirCheck(char * str)
{
	int start = 0;
	int end = strlen(str)-1;
	while(end >= start)
	{
		if(str[end] =='<' )
		{
			str[end] = 0 ;
            end++;
            TrimSpace(str,&end);
            redirStatus = TNPUT_REDIR;
            fileName = str+end;
            break;
		}
        else if(str[end] == '>')
        {
            if(str[end-1] == '>')
            {
			    str[end] = 0 ;
                str[end-1] = 0;
                end++;
                TrimSpace(str,&end);
                redirStatus =APPEND_REDIR;
                fileName = str+end;
                break;

            }
            else
            {
			    str[end] = 0 ;
                end++;
                TrimSpace(str,&end);
                redirStatus = OUTPUT_REDIR;
                fileName = str+end;

                break;
            }
        }
        else
            end--;
	}
}

int main()
{
    char commandLine[COMMANDLINESIZE] = {0};
    InitEnv();
	while(1)
    {
		fileName.clear();
		redirStatus = 0;
        printCmdPrompt();
        if(!inputCmd(commandLine))
            continue;    
        //printf("echo %s\n",commandLine);

		RedirCheck(commandLine);
        
        //printf("echo %s\n",commandLine);
        //printf("%s %s %d\n",commandLine,fileName.c_str(),redirStatus);
        //continue;
        if(CmdParse(commandLine))
			continue;
        //printARG();
        //for(int i = 0;i<argc;i++)
        //{
        //    printf("%s\n",argv[i]);
        //}
        //continue;
		if(CheckAndBuiltin())
			continue;
        doCmd();
   }
    return 0;
}
